package com.vehicle.common.service.datasource;

import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.executor.keygen.SelectKeyGenerator;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.transaction.support.TransactionSynchronizationManager;

import java.util.Locale;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;


/**
 * Created by yantingbin on 2017/7/7.
 */
@Intercepts({
        @Signature(type = Executor.class, method = "update", args = {
                MappedStatement.class, Object.class
        }),
        @Signature(type = Executor.class, method = "query", args = {
                MappedStatement.class, Object.class, RowBounds.class,
                ResultHandler.class
        })
})
public class DynamicPlugin implements Interceptor {

    private static final Logger LOGGER= LoggerFactory.getLogger(DynamicPlugin.class);

    private static final String REGEX=".*insert\\u0020.*|.*delete\\u0020.*|.*update\\u0020.*";

    private static final Map<String,DynamicDataSourceGlobal> CACHE_MAP=new ConcurrentHashMap<>();

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
       boolean synchronizationActive= TransactionSynchronizationManager.isSynchronizationActive();
       if(!synchronizationActive){
           Object[] objects=invocation.getArgs();
           MappedStatement ms=(MappedStatement)objects[0];

           DynamicDataSourceGlobal dynamicDataSourceGlobal;

           if((dynamicDataSourceGlobal=CACHE_MAP.get(ms.getId()))==null){
               dynamicDataSourceGlobal=getDynamicDataSource(ms,objects[1]);
               LOGGER.warn("设置方法[{}] use [{}] Strategy, SqlCommandType [{}]...",ms.getId(),dynamicDataSourceGlobal.name(),ms.getSqlCommandType().name());
               CACHE_MAP.put(ms.getId(),dynamicDataSourceGlobal);
           }
           DynamicDataSourceHolder.putDataSource(dynamicDataSourceGlobal);
       }

       return invocation.proceed();
    }

    @Override
    public Object plugin(Object target) {
       if(target instanceof Executor){
           return Plugin.wrap(target,this);
       }else{
           return target;
       }
    }

    @Override
    public void setProperties(Properties properties) {

    }

    private DynamicDataSourceGlobal getDynamicDataSource(MappedStatement ms,Object parameterObject){
       DynamicDataSourceGlobal dynamicDataSourceGlobal;
       if(ms.getSqlCommandType().equals(SqlCommandType.SELECT)){
           if(ms.getId().contains(SelectKeyGenerator.SELECT_KEY_SUFFIX)){
               dynamicDataSourceGlobal=DynamicDataSourceGlobal.WRITE;
           }else{
               BoundSql boundSql=ms.getSqlSource().getBoundSql(parameterObject);
               String sql=boundSql.getSql().toLowerCase(Locale.CANADA).replaceAll("[\\t\\n\\r]"," ");
               if(sql.matches(REGEX)){
                   dynamicDataSourceGlobal=DynamicDataSourceGlobal.WRITE;
               }else{
                   dynamicDataSourceGlobal=DynamicDataSourceGlobal.READ;
               }
           }
       }else{
           dynamicDataSourceGlobal=DynamicDataSourceGlobal.WRITE;
       }

       return dynamicDataSourceGlobal;
    }
}
